<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <p id="p"></p>
      <button id="name">setName</button>
      <button id="age">setAge</button>
    </div>
    <script>
      let pObj = document.getElementById("p");
      let nameBtn = document.getElementById("name");
      let ageBtn = document.getElementById("age");
      // 初始数据：模拟vue中的data属性，声明的初始数据。
      let user = {
        uname: Date.now(),
        uage: 20,
        uother: {
          a: {
            m: 100,
          },
          b: 200,
        },
      };
      pObj.innerText = JSON.stringify(user);

      // 通过Object.defineProperty实现对user这个对象属性的监听：前提条件，user里面必须已经存在这些属性了。
      // user.uname = "xx" 属性赋值语法糖
      /*
      let newName = "";
      Object.defineProperty(user, "uname", {
        set(newVal) {
          // console.log("uname被修改了: ", newVal);
          newName = newVal;
        }, // 当修改uname属性时，会触发set方法。
        get() {
          return newName;
        }, // 当修改uage属性时，会触发get方法。
        enumerable: true, // 允许该属性uname被遍历
        configurable: true, // 允许该属性被删除
      });

      let newAge = 0;
      Object.defineProperty(user, "uage", {
        set(val) {
          newAge = val;
        }, // 当修改uname属性时，会触发set方法。
        get() {
          return newAge;
        }, // 当修改uage属性时，会触发get方法。
        enumerable: true, // 允许该属性uname被遍历
        configurable: true, // 允许该属性被删除
      });
      */

      // 通过递归实现user对象，每一层对象的属性劫持。
      function deepWatchProperty(target) {
        for (const key in target) {
          let value = target[key];
          if (typeof value !== "object") {
            Object.defineProperty(target, key, {
              set(val) {
                value = val;
                pObj.innerText = JSON.stringify(user);
              },
              get() {
                return value;
              },
              enumerable: true,
              configurable: true,
            });
          } else {
            deepWatchProperty(value);
          }
        }
      }
      deepWatchProperty(user);

      function $$set(target, key, value) {
        target[key] = value;
        pObj.innerText = JSON.stringify(user);
        let value1 = "";
        Object.defineProperty(target, key, {
          set(val) {
            value1 = val;
            pObj.innerText = JSON.stringify(user);
            console.log("----", val);
          },
          get() {
            return value1;
          },
          enumerable: true,
          configurable: true,
        });
      }
      nameBtn.onclick = () => {
        // user.uname = Date.now();
        // user.uother.a.m = 1000;
        $$set(user, "aaa", "测试");
      };
      ageBtn.onclick = () => {
        // user.uage = user.uage + 1;
        user.aaa++;
      };

      // 为什么vue2中，一个对象动态新增的属性或者是动态删除的属性，页面不能够及时响应？
      // 因为动态新增或删除的属性，没有被Object.defineProperty拦截。因此使用$set和$delete动态增删属性，因为$set和$delete实际上就是在设置新属性的Object.defineProperty监听。
    </script>
  </body>
</html>
